#include "../../src/box/service_box.hh"
#include "../../src/detail/scheduler_impl.hh"
#include "../../src/util/os_util.hh"
#include "../../src/util/time_util.hh"
#include "../framework/unittest.hh"
#include <fstream>

FIXTURE_BEGIN(test_scheduler)

using namespace kratos;

SETUP([]() {
  util::make_dir("service");
  std::ofstream ofs;
  ofs.open("config.cfg", std::ios::trunc | std::ios::out);
  ofs << "listener = {"
         "   host = (\"127.0.0.1:10001\")"
         "}"
         "box_channel_recv_buffer_len = 1024 "
         "box_name = \"test_box\""
         "connect_other_box_timeout = 2000 "
         "service_finder_connect_timeout = 2000 "
         "logger_config_line = "
         "\"console://;line=[%y%m%d-%H:%M:%S:%U];flush=true\" "
         "open_coroutine = \"false\"";
  ofs.close();
})

CASE(TestSchedule1) {
  service::ServiceBox sb;
  const char *argv[] = {"test", "--config=config.cfg"};
  ASSERT_TRUE(sb.start(2, argv));
  service::SchedulerImpl sch(&sb);
  bool done = false;
  sch.schedule(
      100,
      [&](std::uint64_t, std::time_t) -> bool {
        done = true;
        return true;
      },
      0);
  std::this_thread::sleep_for(std::chrono::milliseconds(101));
  auto count = sch.do_schedule(util::get_os_time_millionsecond(), 100);
  ASSERT_TRUE(done);
  ASSERT_TRUE(1 == count);
  ASSERT_TRUE(0 == sch.size_noco());
  ASSERT_TRUE(0 == sch.size_co());
  sb.stop();
}

CASE(TestSchedule2) {
  service::ServiceBox sb;
  const char *argv[] = {"test", "--config=config.cfg"};
  ASSERT_TRUE(sb.start(2, argv));
  service::SchedulerImpl sch(&sb);
  bool done = false;
  auto id = sch.schedule(
      100,
      [&](std::uint64_t, std::time_t) -> bool {
        done = true;
        return true;
      },
      0);
  sch.cancel(id);
  std::this_thread::sleep_for(std::chrono::milliseconds(101));
  auto count = sch.do_schedule(util::get_os_time_millionsecond(), 100);
  ASSERT_TRUE(!done);
  ASSERT_TRUE(0 == count);
  ASSERT_TRUE(0 == sch.size_noco());
  ASSERT_TRUE(0 == sch.size_co());
  sb.stop();
}

CASE(TestSchedule3) {
  service::ServiceBox sb;
  const char *argv[] = {"test", "--config=config.cfg"};
  ASSERT_TRUE(sb.start(2, argv));
  service::SchedulerImpl sch(&sb);
  ASSERT_TRUE(0 == sch.schedule(0, nullptr, 0));
  ASSERT_TRUE(0 == sch.size_noco());
  ASSERT_TRUE(0 == sch.size_co());
}

CASE(TestScheduleMany1) {
  service::ServiceBox sb;
  const char *argv[] = {"test", "--config=config.cfg"};
  ASSERT_TRUE(sb.start(2, argv));
  service::SchedulerImpl sch(&sb);
  bool done = false;
  sch.schedule_many(
      100,
      [&](std::uint64_t, std::time_t) -> bool {
        done = true;
        return true;
      },
      0);
  std::this_thread::sleep_for(std::chrono::milliseconds(101));
  auto count = sch.do_schedule(util::get_os_time_millionsecond(), 100);
  ASSERT_TRUE(done);
  ASSERT_TRUE(1 == count);
  done = false;
  std::this_thread::sleep_for(std::chrono::milliseconds(101));
  count = sch.do_schedule(util::get_os_time_millionsecond(), 100);
  ASSERT_TRUE(done);
  ASSERT_TRUE(1 == count);
  ASSERT_TRUE(1 == sch.size_noco());
  ASSERT_TRUE(0 == sch.size_co());
  sb.stop();
}

CASE(TestScheduleMany2) {
  service::ServiceBox sb;
  const char *argv[] = {"test", "--config=config.cfg"};
  ASSERT_TRUE(sb.start(2, argv));
  service::SchedulerImpl sch(&sb);
  bool done = false;
  auto id = sch.schedule_many(
      100,
      [&](std::uint64_t, std::time_t) -> bool {
        done = true;
        return true;
      },
      0);
  std::this_thread::sleep_for(std::chrono::milliseconds(101));
  auto count = sch.do_schedule(util::get_os_time_millionsecond(), 100);
  ASSERT_TRUE(done);
  ASSERT_TRUE(1 == count);
  done = false;
  sch.cancel(id);
  std::this_thread::sleep_for(std::chrono::milliseconds(101));
  count = sch.do_schedule(util::get_os_time_millionsecond(), 100);
  ASSERT_TRUE(!done);
  ASSERT_TRUE(0 == count);
  ASSERT_TRUE(0 == sch.size_noco());
  ASSERT_TRUE(0 == sch.size_co());
  sb.stop();
}

CASE(TestScheduleMany3) {
  service::ServiceBox sb;
  const char *argv[] = {"test", "--config=config.cfg"};
  ASSERT_TRUE(sb.start(2, argv));
  service::SchedulerImpl sch(&sb);
  ASSERT_TRUE(0 == sch.schedule_many(0, nullptr, 0));
  ASSERT_TRUE(0 == sch.size_noco());
  ASSERT_TRUE(0 == sch.size_co());
}

CASE(TestScheduleCo1) {
  service::ServiceBox sb;
  const char *argv[] = {"test", "--config=config.cfg"};
  ASSERT_TRUE(sb.start(2, argv));
  service::SchedulerImpl sch(&sb);
  bool done = false;
  sch.schedule_co(
      100,
      [&](std::uint64_t, std::time_t) -> bool {
        done = true;
        return true;
      },
      0);
  std::this_thread::sleep_for(std::chrono::milliseconds(101));
  sb.update();
  ASSERT_TRUE(done);
  ASSERT_TRUE(0 == sch.size_noco());
  ASSERT_TRUE(0 == sch.size_co());
  sb.stop();
}

CASE(TestScheduleCo2) {
  service::ServiceBox sb;
  const char *argv[] = {"test", "--config=config.cfg"};
  ASSERT_TRUE(sb.start(2, argv));
  service::SchedulerImpl sch(&sb);
  bool done = false;
  auto id = sch.schedule_co(
      100,
      [&](std::uint64_t, std::time_t) -> bool {
        done = true;
        return true;
      },
      0);
  sch.cancel(id);
  std::this_thread::sleep_for(std::chrono::milliseconds(101));
  ASSERT_TRUE(!done);
  ASSERT_TRUE(0 == sch.size_noco());
  ASSERT_TRUE(0 == sch.size_co());
  sb.stop();
}

CASE(TestScheduleCo3) {
  service::ServiceBox sb;
  const char *argv[] = {"test", "--config=config.cfg"};
  ASSERT_TRUE(sb.start(2, argv));
  service::SchedulerImpl sch(&sb);
  ASSERT_TRUE(0 == sch.schedule_co(0, nullptr, 0));
  ASSERT_TRUE(0 == sch.size_noco());
  ASSERT_TRUE(0 == sch.size_co());
}

CASE(TestScheduleCoMany1) {
  service::ServiceBox sb;
  const char *argv[] = {"test", "--config=config.cfg"};
  ASSERT_TRUE(sb.start(2, argv));
  service::SchedulerImpl sch(&sb);
  bool done = false;
  sch.schedule_co_many(
      100,
      [&](std::uint64_t, std::time_t) -> bool {
        done = true;
        return true;
      },
      0);
  std::this_thread::sleep_for(std::chrono::milliseconds(101));
  sb.update();
  ASSERT_TRUE(done);
  ASSERT_TRUE(1 == sch.size_co());
  done = false;
  std::this_thread::sleep_for(std::chrono::milliseconds(101));
  sb.update();
  ASSERT_TRUE(done);
  ASSERT_TRUE(0 == sch.size_noco());
  ASSERT_TRUE(1 == sch.size_co());
  sb.stop();
}

CASE(TestScheduleCoMany2) {
  service::ServiceBox sb;
  const char *argv[] = {"test", "--config=config.cfg"};
  ASSERT_TRUE(sb.start(2, argv));
  service::SchedulerImpl sch(&sb);
  bool done = false;
  auto id = sch.schedule_co_many(
      100,
      [&](std::uint64_t, std::time_t) -> bool {
        done = true;
        return true;
      },
      0);
  std::this_thread::sleep_for(std::chrono::milliseconds(101));
  sb.update();
  ASSERT_TRUE(done);
  ASSERT_TRUE(1 == sch.size_co());
  done = false;
  sch.cancel(id);
  std::this_thread::sleep_for(std::chrono::milliseconds(101));
  sb.update();
  ASSERT_TRUE(!done);
  ASSERT_TRUE(0 == sch.size_co());
  ASSERT_TRUE(0 == sch.size_noco());
  ASSERT_TRUE(0 == sch.size_co());
  sb.stop();
}

CASE(TestScheduleCoMany3) {
  service::ServiceBox sb;
  const char *argv[] = {"test", "--config=config.cfg"};
  ASSERT_TRUE(sb.start(2, argv));
  service::SchedulerImpl sch(&sb);
  ASSERT_TRUE(0 == sch.schedule_co_many(0, nullptr, 0));
  ASSERT_TRUE(0 == sch.size_noco());
  ASSERT_TRUE(0 == sch.size_co());
  ASSERT_TRUE(0 == sch.size());
}

CASE(TestCancel1) {
    service::ServiceBox sb;
    const char* argv[] = { "test", "--config=config.cfg" };
    ASSERT_TRUE(sb.start(2, argv));
    service::SchedulerImpl sch(&sb);
    sch.do_schedule(0, 0);
    sch.cancel(0);
}

CASE(TestSchedule4) {
    service::ServiceBox sb;
    const char* argv[] = { "test", "--config=config.cfg" };
    ASSERT_TRUE(sb.start(2, argv));
    service::SchedulerImpl sch(&sb);
    bool done1 = false;
    sch.schedule(
        100,
        [&](std::uint64_t, std::time_t) -> bool {
            done1 = true;
            return true;
        },
        0);
    bool done2 = false;
    sch.schedule(
        100,
        [&](std::uint64_t, std::time_t) -> bool {
            done2 = true;
            return true;
        },
        0);
    std::this_thread::sleep_for(std::chrono::milliseconds(101));
    auto count = sch.do_schedule(0, 100);
    ASSERT_TRUE(done1);
    ASSERT_TRUE(done2);
    ASSERT_TRUE(count == 2);
    sb.stop();
}

CASE(TestSchedule5) {
    service::ServiceBox sb;
    const char* argv[] = { "test", "--config=config.cfg" };
    ASSERT_TRUE(sb.start(2, argv));
    service::SchedulerImpl sch(&sb);
    bool done = false;
    sch.schedule_many(
        100,
        [&](std::uint64_t, std::time_t) -> bool {
            done = true;
            return false;
        },
        0);
    std::this_thread::sleep_for(std::chrono::milliseconds(101));
    auto count = sch.do_schedule(util::get_os_time_millionsecond(), 100);
    ASSERT_TRUE(done);
    ASSERT_TRUE(1 == count);
    ASSERT_TRUE(0 == sch.size_noco());
    ASSERT_TRUE(0 == sch.size_co());
    sb.stop();
}

CASE(TestSchedule6) {
    service::ServiceBox sb;
    const char* argv[] = { "test", "--config=config.cfg" };
    ASSERT_TRUE(sb.start(2, argv));
    service::SchedulerImpl sch(&sb);
    bool done = false;
    sch.schedule_co_many(
        100,
        [&](std::uint64_t, std::time_t) -> bool {
            done = true;
            return false;
        },
        0);
    std::this_thread::sleep_for(std::chrono::milliseconds(101));
    sb.update();
    ASSERT_TRUE(0 == sch.size_noco());
    ASSERT_TRUE(0 == sch.size_co());
    sb.stop();
}

TEARDOWN([]() {
  util::rm_empty_dir("service");
  kratos::util::remove_file("config.cfg");
})

FIXTURE_END(test_scheduler)
